package org.mindrive.system.util;

import org.apache.poi.openxml4j.util.ZipSecureFile;
import org.mindrive.system.core.MindriveException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

public class ZipUtil {

    private static final Logger log = LoggerFactory.getLogger(ZipUtil.class);

    public static void zip(String destPath, String targetFilePath) {
        try {
            File destFile = new File(destPath);
            if (!destFile.exists()) {
                log.error("压缩源不存在。[{}]", destPath);
                throw new IllegalArgumentException("压缩源不存在。");
            }
            File zipFile = new File(targetFilePath);
            // 创建目标压缩空文件
            if (!zipFile.exists()) {
                File parentFile = zipFile.getParentFile();
                parentFile.mkdirs();
                zipFile.createNewFile();
            }

            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile), Charset.forName("GBK"));
            // 调用实际执行的压缩方法
            zip(out, destFile, destFile.getName());
            // 递归调用最后关闭压缩输出流
            out.close();
        } catch (Exception ex) {
            log.error("压缩文件失败。", ex);
            throw new MindriveException(ex.getMessage());
        }

    }

    public static void zip(List<File> files, String targetFilePath) {

        try {

            File zipFile = new File(targetFilePath);
            // 创建目标压缩空文件
            if (!zipFile.exists()) {
                File parentFile = zipFile.getParentFile();
                parentFile.mkdirs();
                zipFile.createNewFile();
            }

            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile), Charset.forName("GBK"));
            for (File file : files) {
                zip(out, file, file.getName());
            }
            // 递归调用最后关闭压缩输出流
            out.close();
        } catch (Exception ex) {
            log.error("压缩文件失败。", ex);
            throw new MindriveException(ex.getMessage());
        }

    }

    private static void zip(ZipOutputStream zipOut, File inputFile, String base) {
        try {
            // 储存数据缓冲的字节数组
            byte[] bts = new byte[1024];
            // 当前读到的字节个数
            int num = 0;
            // 压缩源是一个文件
            if (inputFile.isFile()) {
                // 写入一个条目，此条目名就是文件名
                zipOut.putNextEntry(new ZipEntry(base));
                // 源文件输入流
                FileInputStream in = new FileInputStream(inputFile);
                // 将源文件的二进制数据读取至bts后，再将bts储存的数据写入当前压缩条目中
                while ((num = in.read(bts)) != -1) {
                    zipOut.write(bts, 0, num);
                }
                // 关闭当前源文件流
                in.close();
            } else {
                // 压缩源是一个目录时，罗列出其下所有文件/文件夹
                File[] subFiles = inputFile.listFiles();
                // 是一个文件夹就将条目名前加一个“/”,“/”表示写入一个目录型的条目
                base = base.length() == 0 ? "" : base + "/";
                // 递归调用
                for (int i = 0; i < subFiles.length; i++) {
                    zip(zipOut, subFiles[i], base + subFiles[i].getName());
                }

            }
        } catch (Exception ex) {
            throw new MindriveException(ex.getMessage());
        }

    }

    public static void unzip(File file, String destPath) {
        log.debug("解压文件：[{}]", destPath);
        ZipInputStream zis = null;
        InputStream inputStream = null;
        try {
            inputStream = new FileInputStream(file);
            ZipSecureFile.setMinInflateRatio(-1.0d);
            zis = new ZipInputStream(inputStream, Charset.forName("GBK"));
            unzip(zis, destPath);
        } catch (Exception ex) {
            log.debug("解压失败，文件不是zip文件。", ex);
            throw new MindriveException("解压失败。文件不是zip文件");
        } finally {
            try {
                if (zis != null) {
                    zis.close();
                }
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                log.error("释放失败");
            }

        }

    }

    private static void unzip(ZipInputStream zis, String destPath) throws Exception {
        ZipEntry entry = null;
        while ((entry = zis.getNextEntry()) != null) {
            if (entry.isDirectory()) { // is dir
                File f = new File(destPath + File.separator + entry.getName());
                System.out.println(entry.getName());
                if (!f.exists()) {
                    f.mkdirs();
                }
            } else { //
                byte[] data = getByte(zis); // 获取当前条目的字节数组
                InputStream is = new ByteArrayInputStream(data); // 把当前条目的字节数据转换成Inputstream流

                Path filePath = Paths.get(entry.getName());
                List<String> names = new ArrayList<String>();
                for (Iterator<Path> it = filePath.iterator(); it.hasNext(); ) {
                    names.add(it.next().toString());
                }
                System.out.println(entry.getName());
                String path = destPath + File.separator;
                for (int i = 0; i < names.size(); i++) {
                    if (i != names.size() - 1) {
                        path += names.get(i) + File.separator;
                        File file = new File(path);
                        if (!file.exists()) {
                            file.mkdirs();
                        }
                    } else {
                        path += names.get(i);
                    }
                }
                File file = new File(path);
                if (!file.exists()) {
                    file.createNewFile();
                    toWrite(is, file);
                }
            }
        }
    }

    private static byte[] getByte(InflaterInputStream zis) {
        try {
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            byte[] temp = new byte[1024];
            byte[] buf = null;
            int length = 0;

            while ((length = zis.read(temp, 0, 1024)) != -1) {
                bout.write(temp, 0, length);
            }

            buf = bout.toByteArray();
            bout.close();
            return buf;
        } catch (IOException e) {
            log.error("取得字节流失败。", e);
            return null;
        }
    }

    //	private static String join(Object[] o, String flag) {
    //		StringBuffer str_buff = new StringBuffer();
    //
    //		for (int i = 0, len = o.length; i < len; i++) {
    //			str_buff.append(String.valueOf(o[i]));
    //			if (i < len - 1)
    //				str_buff.append(flag);
    //		}
    //
    //		return str_buff.toString();
    //	}

    private static void toWrite(InputStream ins, File file) {
        try {
            OutputStream os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            ins.close();
        } catch (Exception e) {
            log.error("写入文件失败。", e);
        }
    }

    /**
     * 替换某个 item,
     *
     * @param zipInputStream  zip文件的zip输入流
     * @param zipOutputStream 输出的zip输出流
     * @param itemName        要替换的 item 名称
     * @param itemInputStream 要替换的 item 的内容输入流
     */
    public static void replaceItem(ZipInputStream zipInputStream, ZipOutputStream zipOutputStream, String itemName,
                                   InputStream itemInputStream) {
        //
        if (null == zipInputStream) {
            return;
        }
        if (null == zipOutputStream) {
            return;
        }
        if (null == itemName) {
            return;
        }
        if (null == itemInputStream) {
            return;
        }
        //
        ZipEntry entryIn;
        try {
            while ((entryIn = zipInputStream.getNextEntry()) != null) {
                String entryName = entryIn.getName();
                ZipEntry entryOut = new ZipEntry(entryName);
                // 只使用 name
                zipOutputStream.putNextEntry(entryOut);
                // 缓冲区
                byte[] buf = new byte[8 * 1024];
                int len;

                if (entryName.equals(itemName)) {
                    // 使用替换流
                    while ((len = (itemInputStream.read(buf))) > 0) {
                        zipOutputStream.write(buf, 0, len);
                    }
                } else {
                    // 输出普通Zip流
                    while ((len = (zipInputStream.read(buf))) > 0) {
                        zipOutputStream.write(buf, 0, len);
                    }
                }
                // 关闭此 entry
                zipOutputStream.closeEntry();

            }
        } catch (IOException e) {
            log.error("替换字节流失败。", e);
        } finally {
            // e.printStackTrace();
            close(itemInputStream);
            close(zipInputStream);
            close(zipOutputStream);
        }
    }

    /**
     * 包装输入流
     */
    public static ZipInputStream wrapZipInputStream(InputStream inputStream) {
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        return zipInputStream;
    }

    /**
     * 包装输出流
     */
    public static ZipOutputStream wrapZipOutputStream(OutputStream outputStream) {
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);
        return zipOutputStream;
    }

    private static void close(InputStream inputStream) {
        if (null != inputStream) {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.error("关闭输入流失败。", e);
            }
        }
    }

    private static void close(OutputStream outputStream) {
        if (null != outputStream) {
            try {
                outputStream.flush();
                outputStream.close();
            } catch (IOException e) {
                log.error("关闭输出流失败。", e);
            }
        }
    }

    public static void main(String[] args) {
        File folder = new File("/Volumes/home/测试数据/市场中心/挂接文件");
        List<File> files = Arrays.asList(folder.listFiles());

        File zipFile = new File("/Volumes/home/测试数据/市场中心/挂接文件.zip");
        ZipUtil.zip(files, zipFile.getAbsolutePath());
    }

}
